package com.thinkingcoder.tcrpc.core.registry.tc;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.thinkingcoder.tcrpc.core.consumer.http.HttpInvoker;
import com.thinkingcoder.tcrpc.core.meta.InstanceMeta;
import com.thinkingcoder.tcrpc.core.meta.ServiceMeta;
import com.thinkingcoder.tcrpc.core.registry.ChangedListener;
import com.thinkingcoder.tcrpc.core.registry.Event;
import com.thinkingcoder.tcrpc.core.registry.RegistryCenter;

import lombok.extern.slf4j.Slf4j;

/**
 * 介入 TcRegistry 的实现类
 */
@Slf4j
public class TcRegistryCenter implements RegistryCenter {

    private static final String REG_PATH = "/reg";
    private static final String UNREG_PATH = "/unReg";
    private static final String FINDALL_PATH = "/findAll";
    private static final String VERSION_PATH = "/version";
    private static final String RENEWS_PATH = "/renews";

    @Value("${tcregistry.servers}")
    private String servers;

    Map<String, Long> VERSIONS = new HashMap<>();
    MultiValueMap<InstanceMeta, ServiceMeta> RENEWS = new LinkedMultiValueMap<>();

    TcHealthChecker healthChecker = new TcHealthChecker();

    @Override
    public void start() {
        log.info(" ===>>> [TcRegistry] : Start with servers: {}", servers);
        // 启动healCheck
        healthChecker.start();
        // 进行 provider 端保活
        providerCheck();
    }

    private void providerCheck() {
        healthChecker.providerCheck(() -> {
            RENEWS.keySet().stream().forEach(instance -> {
                Long timestamp =
                    HttpInvoker.httpPost(renewPath(RENEWS.get(instance)), JSON.toJSONString(instance), Long.class);
                log.info(" ===>>> [TcRegistry] : renew instance {} at {}", instance, timestamp);
            });
        });
    }

    @Override
    public void stop() {
        log.info(" ===>>> [TcRegistry] : Stop with servers: {}", servers);
        healthChecker.stop();
    }

    @Override
    public void register(ServiceMeta service, InstanceMeta instance) {
        log.info(" ===>>> [TcRegistry] : Register instance {} for {}", instance, service);
        HttpInvoker.httpPost(regPath(service), JSON.toJSONString(instance), InstanceMeta.class);
        log.info(" ===>>> [TcRegistry] : Registered instance {} for {}", instance, service);
        RENEWS.add(instance, service);
    }

    @Override
    public void unRegister(ServiceMeta service, InstanceMeta instance) {
        log.info(" ===>>> [TcRegistry] : UnRegister instance {} for {}", instance, service);
        HttpInvoker.httpPost(unRegPath(service), JSON.toJSONString(instance), InstanceMeta.class);
        log.info(" ===>>> [TcRegistry] : UnRegistered instance {} for {}", instance, service);
        RENEWS.remove(instance, service);
    }

    @Override
    public List<InstanceMeta> fetchAll(ServiceMeta service) {
        log.info(" ===>>> [TcRegistry] : Find all instances for {}", service);
        List<InstanceMeta> instances = HttpInvoker.httpGet(findAllPath(service), new TypeReference<>() {});
        log.info(" ===>>> [TcRegistry] : FindAll {}", instances);
        return instances;
    }

    @Override
    public void subscribe(ServiceMeta service, ChangedListener listener) {
        log.info(" ===>>> [TcRegistry] : subscribe service {}", service);
        healthChecker.consumerCheck(() -> {
            Long currentVersion = VERSIONS.getOrDefault(service.toPath(), -1L);
            Long newVersion = HttpInvoker.httpGet(versionPath(service), Long.class);

            log.info(" ===>>> [TcRegistry] : service:{} , currentVersion: {} , newVersion: {}", service, currentVersion,
                newVersion);

            if (newVersion > currentVersion) {
                List<InstanceMeta> instances = fetchAll(service);
                listener.fire(new Event(instances));
                VERSIONS.put(service.toPath(), newVersion);
            }
        });
    }

    private String regPath(ServiceMeta service) {
        return path(REG_PATH, service);
    }

    private String unRegPath(ServiceMeta service) {
        return path(UNREG_PATH, service);
    }

    private String findAllPath(ServiceMeta service) {
        return path(FINDALL_PATH, service);
    }

    private String versionPath(ServiceMeta service) {
        return path(VERSION_PATH, service);
    }

    private String renewPath(List<ServiceMeta> serviceList) {
        return path(RENEWS_PATH, serviceList);
    }

    private String chooseServer() {
        return servers.split(",")[0];
    }

    private String path(String context, ServiceMeta service) {
        return chooseServer() + context + "?serviceName=" + service.toPath();
    }

    private String path(String context, List<ServiceMeta> serviceList) {
        StringBuffer sb = new StringBuffer();
        for (ServiceMeta service : serviceList) {
            sb.append(service.toPath()).append(",");
        }
        String services = sb.toString();
        if (services.endsWith(",")) {
            services = services.substring(0, services.length() - 1);
        }
        return chooseServer() + context + "?serviceNames=" + services;
    }

}
